package com.hhkj.study.api.user.sevice.biz.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.hhkj.captcha.common.manager.CaptchaManager;
import com.hhkj.captcha.model.extend.Captcha;
import com.hhkj.study.admin.course.model.res.CourseRes;
import com.hhkj.study.admin.course.service.extend.CourseExtendService;
import com.hhkj.study.admin.user.entity.User;
import com.hhkj.study.admin.user.model.req.UserReq;
import com.hhkj.study.admin.user.model.res.UserRes;
import com.hhkj.study.admin.user.service.base.UserService;
import com.hhkj.study.admin.user.service.biz.UserBizService;
import com.hhkj.study.admin.user.service.extend.UserExtendService;
import com.hhkj.study.api.user.common.checker.AccountChecker;
import com.hhkj.study.api.user.common.enums.SourceTypeEnum;
import com.hhkj.study.api.user.common.handler.AccountCaptchaPcHandler;
import com.hhkj.study.api.user.common.manager.AccountLoginManager;
import com.hhkj.study.api.user.model.req.*;
import com.hhkj.study.api.user.model.res.*;
import com.hhkj.study.api.user.sevice.biz.ApiCustomerUserBizService;
import com.hhkj.study.common.service.CommonService;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.cont.SysConst;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.GlobalException;
import com.ruoyi.common.model.ThirdPartyRes;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Author luowu
 * @Date 2024/9/26 涵 14:19:15
 */
@Service
public class ApiCustomerUserBizServiceImpl implements ApiCustomerUserBizService {

    @Resource
    private AccountChecker accountChecker;

    @Resource
    private UserExtendService userExtendService;

    @Resource
    private UserService userService;

    @Resource
    private UserBizService userBizService;

    @Resource
    private CommonService commonService;

    @Resource
    private AccountCaptchaPcHandler captchaLoginHandler;

    @Resource
    private AccountLoginManager accountLoginManager;

    @Resource
    private RedisCache redisCache;

    @Resource
    private CaptchaManager captchaManager;

    /** 默认生成用户的昵称前缀 */
    private static final String DEFAULT_USER_PREFIX = "zjwk";

    @Override
    public ApiCustomerUserLoginRes login(ApiCustomerUserLoginReq req) {
        // 1.校验参数
        checkParam(req);
        if ((StrUtil.isBlank(req.getCaptchaCode()) && StrUtil.isBlank(req.getPassword()))
                || (StrUtil.isNotBlank(req.getCaptchaCode()) && StrUtil.isNotBlank(req.getPassword()))) {
            throw new GlobalException("参数错误,验证码或者密码一种方式");
        }
        // 是否验证码方式登录/注册
        boolean captchaFlag = false;
        // 如果有验证码验证码是否有效
        if (StrUtil.isNotBlank(req.getCaptchaCode())) {
            if (StrUtil.isBlank(req.getMobile())) {
                throw new GlobalException("手机号不能为空");
            }
            String sourceCode = Constants.PC_SOURCE_CODE;
            if (SourceTypeEnum.WECHAT_MINI_PROGRAM.getCode().equals(req.getSourceType())) {
                sourceCode = Constants.WX_SOURCE_CODE;
            }
            captchaLoginHandler.verify(req.getMobile(), sourceCode, req.getCaptchaCode());
            captchaFlag = true;
        }
        User customerUser = userExtendService.getByMobileOrIdCard(req.getMobile(), null);
        // 验证码登录/注册,有用户直接返回用户不存在新注册
        if (ObjectUtil.isNull(customerUser) && captchaFlag) {
            // 用户不存在，则注册
            customerUser = saveCustomerUser(req);
        }
        if (ObjectUtil.isNull(customerUser) || SysConst.Flag.IS.getFlag().equals(customerUser.getDisableFlag())) {
            throw new GlobalException("账号不存在或已禁用！");
        }
        // 密码是否正确
        if (StringUtils.isNotBlank(req.getPassword())) {
            if (!SecurityUtils.matchesPassword(req.getPassword(), customerUser.getPassword())) {
                throw new GlobalException("用户名或密码错误！");
            }
        }
        return accountLoginManager.doLogin(customerUser, req.getSourceType());
    }

    /**
     * 小程序登录目前集成在登录接口,该接口暂时不做处理
     * @param req
     * @return
     */
    @Override
    public ApiCustomerUserWxLoginRes wxLogin(ApiCustomerUserWxLoginReq req) {
        // 校验参数
        checkParam(req);
        // 如果有验证码验证码是否有效 此方式为小程序中直接手机号注册方式会发验证码
        if (StrUtil.isNotBlank(req.getCaptchaCode())) {
            if (StrUtil.isBlank(req.getMobile())) {
                throw new GlobalException("手机号不能为空");
            }
            captchaLoginHandler.verify(req.getMobile(), Constants.WX_SOURCE_CODE, req.getCaptchaCode());
        }
        // code获取用户凭据
        ThirdPartyRes wxUserAuthRes = commonService.getThirdPartyCert(req.getWxAuthCode());
        String thirdPartyId = wxUserAuthRes.getThirdPartyId();
        if (StrUtil.isBlank(thirdPartyId)) {
            throw new GlobalException("微信获取用户凭据失败");
        }

        // 微信扫码/小程序登录一般首次获取手机号码,后续直接拿到微信凭据不再获取手机号
        User customerUser = userExtendService.getByThirdPartyId(thirdPartyId);
        if (ObjectUtil.isNull(customerUser)) {
            if (StrUtil.isBlank(req.getMobile())) {
                return null;
            } else {
                req.setThirdPartyId(thirdPartyId);
                // 新用户注册
                customerUser = saveCustomerUser(req);
            }
        }
        if (ObjectUtil.isNotNull(customerUser) && SysConst.Flag.IS.getFlag().equals(customerUser.getDisableFlag())) {
            throw new GlobalException("账号已禁用！");
        }
        // 默认小程序内直接登录来源
        Integer sourceType = SourceTypeEnum.WECHAT_MINI_PROGRAM.getCode();
        if (StrUtil.isNotBlank(req.getScene())) {
            sourceType = SourceTypeEnum.WECHAT_SCAN.getCode();
        }
        ApiCustomerUserWxLoginRes customerUserLoginRes = accountLoginManager.doWxLogin(customerUser, sourceType);
        // 扫码登录/注册需要将唯一码和扫码用户关联
        assocScanUser(req.getScene(), customerUserLoginRes.getSessionPcKey());
        return customerUserLoginRes;
    }

    @Override
    public ApiCustomerUserGetRes getInfo() {
        // 校验账号是否合法
        ApiCustomerUserBaseRes currentCustomerUser = accountChecker.checkAccountValid();
        UserRes userRes = userBizService.getById(currentCustomerUser.getId());
        return BeanUtil.toBean(userRes, ApiCustomerUserGetRes.class);
    }

    @Override
    public boolean update(ApiCustomerUserUpdateReq req) {
        // 校验账号是否合法
        ApiCustomerUserBaseRes currentCustomerUser = accountChecker.checkAccountValid();
        req.setId(currentCustomerUser.getId());
        // TODO 还需要修改缓存中个人信息的名称
        return userBizService.update(req);
    }

    @Override
    public boolean verifyMobile(ApiCustomerUserBaseReq req) {
        if (StrUtil.isBlank(req.getMobile()) || StrUtil.isBlank(req.getCaptchaCode())) {
            throw new GlobalException("参数错误,手机号和验证码不能为空");
        }
        // 校验账号是否合法
        accountChecker.checkAccountValid();
        captchaLoginHandler.verify(req.getMobile(), Constants.UPDATE_PWD_SOURCE_CODE, req.getCaptchaCode());
        return true;
    }

    @Override
    public boolean updatePwd(ApiCustomerUserPwdUpdateReq req) {
        // 校验账号是否合法
        ApiCustomerUserBaseRes currentUser = accountChecker.checkAccountValid();
        // 防止直接通过接口修改密码
        Captcha captcha = captchaManager.getCaptcha(Constants.UPDATE_PWD_SOURCE_CODE, req.getMobile());
        if (ObjectUtil.isNull(captcha)) {
            throw new GlobalException("调用方式错误,请按正常流程操作");
        }
        if (!StrUtil.equals(req.getAgainPassword(), req.getPassword())) {
            throw new GlobalException("参数错误,两次密码要一致");
        }

        UserRes userRes = userBizService.getById(currentUser.getId());
        if (SecurityUtils.matchesPassword(req.getPassword(), userRes.getPassword())) {
            throw new GlobalException("新密码不能与老密码一致");
        }
        // 修改密码
        LambdaUpdateWrapper<User> userUpdateWrapper = new LambdaUpdateWrapper<>();
        userUpdateWrapper.set(User::getPassword, SecurityUtils.encryptPassword(req.getPassword())).eq(User::getId, currentUser.getId());
        return userService.update(userUpdateWrapper);
    }

    /**
     * 关联扫码用户
     * @param scene
     * @param sessionKey
     */
    private void assocScanUser(String scene, String sessionKey){
        // 用户扫码后,前端轮询扫码状态,获得sessionKey查询用户信息接口跳转
        if (StrUtil.isNotBlank(scene)) {
            String key = RedisUtils.createKey(Constants.WX_SCAN_KEY_PREFIX, scene);
            Object cacheObject = redisCache.getCacheObject(key);
            if (ObjectUtil.isNull(cacheObject)) {
                return;
            }
            redisCache.setCacheObject(key, sessionKey, Constants.TWO_MINUTES, TimeUnit.MINUTES);
        }
    }


    /**
     * 新增用户
     * @param userBaseReq
     */
    private User saveCustomerUser(ApiCustomerUserBaseReq userBaseReq) {
        User customerUser = new User();
        // 默认新用户为随机昵称
        String randomName = StrUtil.format("{}_{}", DEFAULT_USER_PREFIX, RandomUtil.randomNumbers(9));
        customerUser.setName(randomName);
        customerUser.setNickName(randomName);
        customerUser.setMobile(userBaseReq.getMobile());
        customerUser.setWxId(userBaseReq.getThirdPartyId());
        customerUser.setCreateBy(0L);
        // 新增用户
        userService.save(customerUser);
        return customerUser;
    }

    /**
     * 校验参数是否合法
     * @param req
     */
    private void checkParam(ApiCustomerUserBaseReq req) {
        // 手机号合法判断
        if (StrUtil.isNotBlank(req.getMobile())) {
            if (!PhoneUtil.isPhone(req.getMobile())) {
                throw new GlobalException("手机号码有误");
            }
        }
        if (StrUtil.isNotBlank(req.getPassword())) {
            if (req.getPassword().length() < 6 || req.getPassword().length() > 16) {
                throw new GlobalException("密码长度只能为6-16位");
            }
        }
    }
}
